#include <asm-generic/errno-base.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>

union semun {
  int val;               /* Value for SETVAL */
  struct semid_ds *buf;  /* Buffer for IPC_STAT, IPC_SET */
  unsigned short *array; /* Array for GETALL, SETALL */
  struct seminfo *__buf; /* Buffer for IPC_INFO
                            (Linux-specific) */
};
void sem_p(int, int);
void sem_v(int, int);

void *sen(void *);
void *rev(void *);

int main(int argc, char *argv[]) {
  pthread_t tid_1, tid_2;
  pthread_create(&tid_1, NULL, sen, NULL);
  pthread_create(&tid_2, NULL, rev, NULL);

  pthread_join(tid_1, NULL);
  pthread_join(tid_2, NULL);

  pthread_exit(NULL);
}

void *sen(void *argv) {
  key_t key;
  key = ftok(".", 2);
  if (key < 0) {
    perror("ftok fail");
    exit(-1);
  }

  int sem_id = semget(key, 2, IPC_CREAT | IPC_EXCL | 0666);
  if (sem_id < 0) {
    if (errno == EEXIST) {
      sem_id = semget(key, 2, 0666);
      if (sem_id < 0) {
        perror("semget fail");
        exit(-1);
      }
    } else {
      perror("semget fail");
      exit(-1);
    }
  } else if (sem_id > 0) {
    union semun a;
    a.val = 0;
    semctl(sem_id, 0, SETVAL, a);
    a.val = 1;
    semctl(sem_id, 1, SETVAL, a);
  }

  int shm_id = shmget(key, 1024, IPC_CREAT | IPC_EXCL | 0666);
  if (shm_id < 0) {
    if (errno == EEXIST) {
      shm_id = shmget(key, 1024, 0666);
    } else {
      perror("shmget fail");
      exit(-1);
    }
  }

  char *shm_buf = shmat(shm_id, NULL, 0);
  if (shm_buf == (void *)-1) {
    perror("shmat fail");
    exit(-1);
  }

  while (1) {
    sem_p(sem_id, 0);
    printf("please input:");
    scanf("%s", shm_buf);
    sem_v(sem_id, 1);

    if (!strcmp(shm_buf, "bye"))
      break;
  }

  semctl(sem_id, 0, IPC_RMID, 0);
  pthread_exit(NULL);
}
void *rev(void *argv) {
  key_t key;
  key = ftok(".", 1);
  if (key < 0) {
    perror("ftok fail");
    exit(-1);
  }

  int sem_id = semget(key, 2, IPC_CREAT | IPC_EXCL | 0666);
  if (sem_id < 0) {
    if (errno == EEXIST) {
      sem_id = semget(key, 2, 0666);
      if (sem_id < 0) {
        perror("semget fail");
        exit(-1);
      }
    } else {
      perror("semget fail");
      exit(-1);
    }
  } else if (sem_id > 0) {
    union semun a;
    a.val = 0;
    semctl(sem_id, 0, SETVAL, a);
    a.val = 1;
    semctl(sem_id, 1, SETVAL, a);
  }

  int shm_id = shmget(key, 1024, IPC_CREAT | IPC_EXCL | 0666);
  if (shm_id < 0) {
    if (errno == EEXIST) {
      shm_id = shmget(key, 1024, 0666);
    } else {
      perror("shmget fail");
      exit(-1);
    }
  }

  char *shm_buf = shmat(shm_id, NULL, 0);
  if (shm_buf == (void *)-1) {
    perror("shmat fail");
    exit(-1);
  }

  while (1) {
    sem_p(sem_id, 1);
    printf("read is %s\n", shm_buf);
    sem_v(sem_id, 0);

    if (!strcmp(shm_buf, "bye"))
      break;
  }

  semctl(sem_id, 0, IPC_RMID, 0);
  pthread_exit(NULL);
}

void sem_p(int sem_id, int num) {
  struct sembuf a;
  a.sem_num = num;
  a.sem_flg = 0;
  a.sem_op = -1;
  semop(sem_id, &a, 1);
}
void sem_v(int sem_id, int num) {
  struct sembuf a;
  a.sem_num = num;
  a.sem_flg = 0;
  a.sem_op = 1;
  semop(sem_id, &a, 1);
}
